import dayjs from 'dayjs';
import ms from 'ms';
import pkg from '../../../package.json';
import { TASK_TYPE_CATEGORY_MAP } from '../../common/constants';
import errorCodesBuilder from '../helpers/errorCodesBuilder';
import { dealWithBalance } from '../plugins/billing';
import { datetime } from '../../common/formatter';
const { name } = pkg;
const errorCodes = errorCodesBuilder();

export default async function sendAlert({
	task,
	job,
	content, // 邮件内容
	summary, // 微信内容
	detail, // 站内信内容
	title,
	smsTemplate, // 短信模版
	link = '', // 微信链接
}) {
	const {
		methods,
		app: {
			redis,
			model: { AlertGroup, NotificationSendLog },
			config: {
				[name]: { alertSign },
			},
		},
		log,
	} = this;
	const {
		uid,
		taskType,
		alertSettings: {
			notification: notificationType,
			emailAllowedTime,
			smsAllowedTime,
			alertGroupIds,
		},
	} = task;
	const sendEmail = dealWithBalance({
		uid,
		ops: [
			{
				key: 'emailAlert',
				value: -1,
			},
		],
		server: this,
	})(methods.sendEmail);
	const sendWechatMessage = methods.sendWechatMessage;
	const sendSMS = dealWithBalance({
		uid,
		ops: [
			{
				key: 'smsAlert',
				value: -1,
			},
		],
		server: this,
	})(methods.sendSMS);

	while (
		await redis.get(`dailyAlert:${job._id.toString()}:lock`).catch(() => 1)
	) {
		log.warn({ jobId: job._id }, 'daily alert locked');
		await new Promise(resolve => setTimeout(resolve, Math.random() * 5000));
	}

	let jobTodayAlert;
	try {
		await redis.set(`dailyAlert:${job._id.toString()}:lock`, 1, 'EX', 60);
		jobTodayAlert = await redis.get(`dailyAlert:${job._id.toString()}`);
		jobTodayAlert = JSON.parse(jobTodayAlert);
		const currentTimestamp = todayStartTime();
		if (!jobTodayAlert || jobTodayAlert.timestamp !== currentTimestamp) {
			log.debug(
				{ jobTodayAlert, currentTimestamp, jobId: job._id },
				'初始化dailyAlertCache'
			);
			jobTodayAlert = {
				timestamp: currentTimestamp,
				smsCount: 0,
				emailCount: 0,
			};
			await redis.set(
				`dailyAlert:${job._id.toString()}`,
				JSON.stringify(jobTodayAlert)
			);
		}
		log.debug({ jobTodayAlert, jobId: job._id }, '获取dailyAlertCache');
	} catch (error) {
		log.error({ error, jobId: job._id }, '获取dailyAlertCache失败');
	}

	let isSendSms = false;
	let isSendEmail = false;
	let receivers = {};
	try {
		const groups = await AlertGroup.find({
			_id: { $in: alertGroupIds },
		})
			.populate('receiverIds')
			.populate('uid');
		groups.forEach(async g => {
			if (g.includeWechat) {
				// eslint-disable-next-line camelcase
				const wechatId = g.uid?.wechat?.wx_openid;
				if (g.uid?.wechat && wechatId) {
					receivers[wechatId] = {
						type: 'wechat',
						contcat: wechatId,
						status: 'pending',
					};
				}
			}
			g.receiverIds.forEach(r => {
				['email', 'phone'].forEach(type => {
					const contcat = r[type];
					if (!contcat || !r[`${type}Verified`]) return;
					receivers[contcat] = {
						type,
						contcat,
						status: 'pending',
					};
				});
			});
		});
	} catch (err) {
		const error = errorCodes['[0E][发送告警邮件]查询告警组失败'].from(err);
		log.error({ error, title, taskId: task._id }, error.message);
		throw error;
	}
	receivers = Object.values(receivers);

	let notification;
	try {
		notification = new NotificationSendLog({
			title,
			summary,
			receivers,
			content,
			taskId: task._id,
			refId: task.refId,
			domain: task.domain,
			type: 'monitorException',
			taskCategory: TASK_TYPE_CATEGORY_MAP[taskType],
			taskType: taskType,
			target: task.target,
			uid: task.uid,
			endTime: task.endTime,
		});
		notification.save();
	} catch (err) {
		const error = errorCodes['[0E][发送告警邮件]创建告警发送记录失败'].from(
			err
		);
		log.error({ error, taskId: task._id }, error.message);
		throw error;
	}

	const isScheduleTask =
		task.triggerType === 'schedule' || task.triggerType === 'scheduleIncrement';
	const smsLimied =
		isScheduleTask &&
		jobTodayAlert?.smsCount >= job.alertSettings.smsDailyLimit;
	const emailLimied =
		isScheduleTask &&
		jobTodayAlert?.emailCount >= job.alertSettings.emailDailyLimit;

	/**
	 * 告警方式为邮件且告警类型中当前任务类型为true
	 */
	const sends = receivers.reduce(
		(arr, { type, contcat }) => {
			let sender;
			if (!emailLimied && type === 'email' && notificationType.email) {
				if (!emailAllowedTime) {
					log.error({ task }, '发送告警emailAllowedTime为空');
				}
				const startTime = dayjs(
					emailAllowedTime?.start || '00:00:00',
					'hh:mm:00'
				).toDate();
				const endTime = dayjs(
					emailAllowedTime?.end || '23:59:59',
					'hh:mm:59'
				).toDate();
				const now = new Date();
				if (now < startTime || now > endTime) return;
				sender = sendEmail({
					to: contcat,
					content,
					title,
				}).then(x => (isSendEmail = true));
			}
			if (!smsLimied && type === 'phone' && notificationType.sms) {
				const startTime = dayjs(
					smsAllowedTime?.start || '00:00:00',
					'hh:mm:00'
				).toDate();
				const endTime = dayjs(
					smsAllowedTime?.end || '23:59:59',
					'hh:mm:59'
				).toDate();
				const now = new Date();
				if (now < startTime || now > endTime) return;
				sender = sendSMS({
					to: contcat,
					templateId: smsTemplate.id,
					content: smsTemplate.value,
				}).then(x => (isSendSms = true));
			}
			if (type === 'wechat' && notificationType.wechat) {
				sender = sendWechatMessage({
					url: link,
					wxOpenId: contcat,
					data: {
						first: {
							value: `${alertSign}${title}`,
						},
						content: {
							value: summary,
						},
						occurtime: {
							value: datetime(task.endTime),
						},
						remark: {
							value: '登录ScanV控制台查看详情。',
						},
					},
				});
			}

			if (!sender) return arr;

			arr.push(
				sender
					.then(() => ({
						contcat,
						type,
						status: 'success',
					}))
					.catch(err => {
						const error = errorCodes['[0E][发送告警邮件]发送告警邮件失败'].from(
							err
						);
						log.error(
							{ error, contcat, title, content, uid: task.uid },
							error.message
						);
						return {
							contcat,
							type,
							status: 'fail',
						};
					})
			);

			return arr;
		},
		[
			notificationType.inBox
				? methods.sendNotification({
						title,
						category: '告警通知',
						to: [task.uid],
						message: summary,
						data: detail,
				  })
				: Promise.resolve(),
		]
	);

	const result = await Promise.all(sends);
	receivers = result.reduce((receiver, res) => {
		receiver = receiver.contcat(res);
		return receiver;
	}, []);
	log.log(
		{
			receivers,
			emailLimied,
			smsLimied,
			notificationId: notification._id,
			taskId: task._id,
		},
		'告警发送完成'
	);

	try {
		const updateInfo = {
			receivers,
			status: receivers.every(item => item?.status === 'success')
				? 'success'
				: 'fail',
		};

		await NotificationSendLog.updateOne(
			{ _id: notification._id },
			{ $set: updateInfo }
		);

		if (isScheduleTask) {
			await redis.set(
				`dailyAlert:${job._id.toString()}`,
				JSON.stringify({
					...jobTodayAlert,
					smsCount: (jobTodayAlert?.smsCount || 0) + (isSendSms ? 1 : 0),
					emailCount: (jobTodayAlert?.emailCount || 0) + (isSendEmail ? 1 : 0),
				})
			);
		}
		await redis.del(`dailyAlert:${job._id.toString()}:lock`);
	} catch (err) {
		const error = errorCodes['[0E][发送告警信息]更新告警发送记录失败'].from(
			err
		);
		log.error({ error, receivers, _id: notification._id }, error.message);
		throw error;
	}

	return receivers;
}

function todayStartTime() {
	const now = Date.now();
	return now - (now % ms('1d'));
}
